#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // 除DEBUG模式外，禁用Windows窗口

use config::AppLauncherUpdaterConfig;
use encoding_rs::{GBK, UTF_8};
use sha2::{Digest, Sha256};
use std::error::Error;
use std::io::{BufRead, BufReader, Read, Write};
use std::os::windows::process::CommandExt;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::time::{Duration, SystemTime};
use std::{fs, io, thread};
use std::any::Any;

mod config;
mod msg;

const CREATE_NO_WINDOW: u32 = 0x08000000; // 无窗口模式
const DETACHED_PROCESS: u32 = 0x00000008; // 分离进程模式?

fn main() {
    println!("APP-LAUNCHER-UPDATER");

    let result = run();
    match result {
        Ok(_) => return,
        Err(e) => {
            msg::error(format!("错误: {}", e).as_str());
        }
    }
}

fn run() -> Result<(), Box<dyn Error>> {
    let config_load_result = AppLauncherUpdaterConfig::load();
    if let Err(msg) = config_load_result {
        return Err(Box::from(
            format!(
                "{} {} {}",
                "无法加载配置文件",
                config::CONFIG_FILE_NAME,
                msg
            )
            .as_str(),
        ));
    }
    let config = AppLauncherUpdaterConfig::load().ok().unwrap();

    // 检查是否有新版已经下载完毕，且能通过运行校验
    let new_version = exists_new_version_and_validated(
        config.launcher_update_file_name(),
        config.validate_exec_command(),
        config.validate_exec_response(),
    );

    if new_version {
        // 若检测到新版程序，且能通过运行校验
        // 备份旧程序，同时将新程序更换到当前版本
        let replace_op_result = backup_and_replace_new_version(
            config.launcher_backup_file_name(),
            config.launcher_file_name(),
            config.launcher_update_file_name(),
        );
        if let Err(e) = replace_op_result {
            msg::error(format!("更新启动器失败: {}", e).as_str());
        }
    }

    let server_download_url = config.server_path();
    println!("{}", server_download_url);

    // 从服务端下载app-launcher.jar
    let buffer = download_app_launcher(server_download_url)?;
    // 计算服务端下载的app-launcher.jar的hash值
    let server_side_hash = cal_hash_from_buffer(&buffer);
    println!("server_side_hash {}", server_side_hash);
    // 计算本地当前版本的app-launcher.jar的hash值
    let client_side_hash = cal_hash_from_file_name("./ghfs.exe").unwrap();
    println!("client_side_hash {}", client_side_hash);

    if server_side_hash.eq(&client_side_hash) {
        // 服务端版本与本地一致，不做处理
        return Ok(());
    }

    // 否则下载更新
    let file_name = "ghfs.exe";
    match save_new_version(file_name, &buffer) {
        Ok(_) => {}
        Err(e) => {
            msg::error(format!("检测到app-launcher有更新，但下载到本地失败，原因: {}", e).as_str())
        }
    }

    Ok(())
}

/// 备份旧版程序且将新版程序更新到路径下
fn backup_and_replace_new_version(
    backup_file_name: &str,
    launcher_file_name: &str,
    new_file_name: &str,
) -> Result<(), Box<dyn Error>> {
    fs::rename(launcher_file_name, backup_file_name)?; // 尝试备份当前版本
    fs::rename(new_file_name, launcher_file_name)?; // 使用新版
    Ok(())
}

/// 检查是否有新版本，且是否能通过可执行校验
fn exists_new_version_and_validated(
    launcher_file_name: &str,
    validate_exec_command: &str,
    validate_exec_response: &str,
) -> bool {
    let new_version_file_exists = fs::exists(launcher_file_name);
    match new_version_file_exists {
        Ok(exists_new_version) => {
            if !exists_new_version {
                return false;
            }

            validate_new_version_can_exec(validate_exec_command, validate_exec_response) // 校验是否可执行
        }
        Err(_) => false,
    }
}

/// 校验是否可执行
fn validate_new_version_can_exec(
    validate_exec_command: &str,
    validate_exec_response: &str,
) -> bool {
    let output = if cfg!(target_os = "windows") {
        let charset = msg::get_oemcp(); // 获取cmd默认字符集

        // windows 环境处理
        let (tx, rx) = mpsc::channel();

        thread::spawn(move || {
            println!("test");
            let mut command = Command::new("ping");

            command.args(["baidu.com"]);
            command.creation_flags(CREATE_NO_WINDOW);

            let mut child = command
                .stdout(Stdio::piped())
                .stderr(Stdio::piped())
                .spawn()
                .unwrap();

            let stdout = child.stdout.as_mut().unwrap();
            let mut stdout_reader = BufReader::new(stdout);
            // let stdout_lines = stdout_reader.lines();

            loop {
                let mut buf = vec![];
                stdout_reader.read_until(0xA, &mut buf);
                if buf.len() == 0 {
                    print!("Process END");
                    break;
                }

                // 简陋的中文Windows默认GBK处理
                if charset == 936 {
                    // GBK
                    let line = GBK.decode(&buf).0.to_string();
                    tx.send(line).unwrap();
                } else {
                    // UTF-8
                    let line = UTF_8.decode(&buf).0.to_string();
                    tx.send(line).unwrap();
                }
            }
            println!("process exited");
        });

        let task_start_time = SystemTime::now();
        loop {
            let duration = SystemTime::now()
                .duration_since(task_start_time)
                .unwrap();

            if duration > Duration::from_secs(10) {
                // 超过4s，认为超时
                break;
            }

            match rx.recv() { // 阻塞式的读取
                Ok(line) => {
                    println!("receive {}", line);
                },
                Err(err) => {
                    if err.to_string().eq("receiving on a closed channel") { break }
                }
            }
            // 从管道读取上面线程正在执行的任务
        }
    } else {
        // 其他系统平台未实现
        todo!()
    };
    false
}

fn save_new_version(file_name: &str, buffer: &[u8]) -> Result<(), Box<dyn Error>> {
    // 检查并删除已存在的文件
    if fs::metadata(file_name).is_ok() {
        fs::remove_file(file_name)?;
    }

    // 创建新文件并写入内容
    fs::write(file_name, buffer)?;

    Ok(())
}

/// 计算传入的二进制数据的sha256值
fn cal_hash_from_buffer(buffer: &[u8]) -> String {
    let mut sha = Sha256::new();
    sha.update(buffer);
    let result = sha.finalize();
    // 转换为十六进制字符串
    let hash_hex = format!("{:x}", result);
    println!("SHA-256: {}", hash_hex);
    String::from(hash_hex)
}

fn cal_hash_from_file_name(file_name: &str) -> Result<String, Box<dyn Error>> {
    // 检查是否存在
    if !fs::exists(file_name)? {
        return Ok("".parse().unwrap());
    }
    let buffer = fs::read(file_name)?;
    Ok(cal_hash_from_buffer(&buffer))
}

/// HTTP从服务端下载最新的程序
fn download_app_launcher(url: &str) -> Result<Vec<u8>, Box<dyn Error>> {
    let mut res = reqwest::blocking::get(url)?;
    let mut buffer = Vec::<u8>::new();
    res.read_to_end(&mut buffer)?;
    Ok(buffer)
}
